function init() {
    var objGo = go.GraphObject.make;  // for conciseness in defining templates

    var contextMenu =
        objGo("ContextMenu",
            objGo("ContextMenuButton",
                objGo(go.TextBlock, "发布"),
                {
                    click: function (e, obj) {
                        menu("发布",e,obj);
                    }
                }),
            objGo("ContextMenuButton",
                objGo(go.TextBlock, "测试"),
                {
                    click: function (e, obj) {
                        console.log(this.selectedNode)
                        menu("测试",e,obj);
                    }
                }),
            {width:100}
        );

    myDiagram =
        objGo(go.Diagram, "myDiagramDiv",  // must name or refer to the DIV HTML element
            {
                grid: objGo(go.Panel, "Grid",
                    objGo(go.Shape, "LineH", { stroke: "lightgray", strokeWidth: 0.5 }),
                    objGo(go.Shape, "LineH", { stroke: "gray", strokeWidth: 0.5, interval: 10 }),
                    objGo(go.Shape, "LineV", { stroke: "lightgray", strokeWidth: 0.5 }),
                    objGo(go.Shape, "LineV", { stroke: "gray", strokeWidth: 0.5, interval: 10 })
                ),
                initialContentAlignment: go.Spot.TopCenter, // 居中显示内容
                allowDrop: true,  // must be true to accept drops from the Palette
                "undoManager.isEnabled": true
            });
    myDiagram.nodeTemplate =
        objGo(go.Node, "Spot",
            { locationSpot: go.Spot.Center },
            new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
            new go.Binding("json", "json").makeTwoWay(),
            objGo(go.Panel, "Auto",
                new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify),
                objGo(go.Shape, "Rectangle",  // default figure
                    {
                        portId: "",
                        cursor: "pointer",
                        strokeWidth: 2
                    },new go.Binding("fill","background").makeTwoWay()
                    ,new go.Binding("fromLinkable","fromLinkable").makeTwoWay()
                    ,new go.Binding("toLinkable","toLinkable").makeTwoWay()),
                objGo(go.TextBlock,
                    {
                        margin: 8,
                        maxSize: new go.Size(160, NaN),
                        wrap: go.TextBlock.WrapFit,
                        //editable: true
                    },
                    new go.Binding("text").makeTwoWay())
            ),
            {doubleClick : function (e, node){nodeDoubleClick(e, node);}},
            {contextMenu:contextMenu}
        );

    myDiagram.linkTemplate =
        objGo(go.Link,
            {
                routing: go.Link.Normal,
                curve: go.Link.JumpOver,
            },
            new go.Binding("points").makeTwoWay(),
            objGo(go.Shape,  // the link path shape
                { isPanelMain: true, strokeWidth: 2 }),
            objGo(go.Shape,  // the arrowhead
                { toArrow: "Standard", stroke: null })
        );


    //添加监听线生成事件
    myDiagram.addDiagramListener("LinkDrawn", function(e) {
        //一个节点只能一根线
        var node = myDiagram.findNodeForKey(e.subject.data.from);
        if(node.findLinksOutOf().count>1) {
            myDiagram.model.removeLinkData(e.subject.data);
        }
    })
    //监听节点生成事件
    var nodeIndex=0;
    myDiagram.addDiagramListener("externalobjectsdropped", function(e) {
        e.subject.each(function(n){
            nodeIndex++;
            var nodeData= myDiagram.model.findNodeDataForKey(n.data.key);
            myDiagram.model.setDataProperty(nodeData, 'text', n.data.text+nodeIndex);
            nodeData.json.text=n.data.text+nodeIndex;
        });
    })

    myPalette =
        objGo(go.Palette, "myPaletteDiv",
            {
                maxSelectionCount: 1,
                nodeTemplateMap: myDiagram.nodeTemplateMap,  // share the templates used by myDiagram
                model: new go.GraphLinksModel([  // specify the contents of the Palette
                    Object.create(customNodeTemplate.jenkins),
                    Object.create(customNodeTemplate.apollo),
                    Object.create(customNodeTemplate.sql),
                    Object.create(customNodeTemplate.notify),
                    Object.create(customNodeTemplate.shell)
                ])
            });


    //load();  // load an initial diagram from some JSON text
    myDiagram.model.nodeDataArray=[Object.create(customNodeTemplate.start), Object.create(customNodeTemplate.end)];


}


function initWithContextMenu(contextMenuFlag) {
    var objGo = go.GraphObject.make;  // for conciseness in defining templates


    var nodeTemplate;


    if(contextMenuFlag){
        //发布功能，只能从发布管理页面操作，只支持测试
        var contextMenu =
            objGo("ContextMenu",
                objGo("ContextMenuButton",
                    objGo(go.TextBlock, "测试"),
                    {
                        click: function (e, obj) {
                            console.log(this.selectedNode)
                            menu("测试",e,obj);
                        }
                    }),
                {width:100}
            );
        nodeTemplate=objGo(go.Node, "Spot",
            { locationSpot: go.Spot.Center },
            new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
            new go.Binding("json", "json").makeTwoWay(),
            objGo(go.Panel, "Auto",
                new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify),
                objGo(go.Shape, "Rectangle",  // default figure
                    {
                        portId: "",
                        cursor: "pointer",
                        strokeWidth: 2
                    },new go.Binding("fill","background").makeTwoWay()
                    ,new go.Binding("fromLinkable","fromLinkable").makeTwoWay()
                    ,new go.Binding("toLinkable","toLinkable").makeTwoWay()),
                objGo(go.TextBlock,
                    {
                        margin: 8,
                        maxSize: new go.Size(160, NaN),
                        wrap: go.TextBlock.WrapFit,
                        //editable: true
                    },
                    new go.Binding("text").makeTwoWay())
            ),
            {doubleClick : function (e, node){nodeDoubleClick(e, node);}},
            {contextMenu:contextMenu}
        );
    }else{
        nodeTemplate=objGo(go.Node, "Spot",
            { locationSpot: go.Spot.Center },
            new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
            new go.Binding("json", "json").makeTwoWay(),
            objGo(go.Panel, "Auto",
                new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify),
                objGo(go.Shape, "Rectangle",  // default figure
                    {
                        portId: "",
                        cursor: "pointer",
                        strokeWidth: 2
                    },new go.Binding("fill","background").makeTwoWay()
                    ,new go.Binding("fromLinkable","fromLinkable").makeTwoWay()
                    ,new go.Binding("toLinkable","toLinkable").makeTwoWay()),
                objGo(go.TextBlock,
                    {
                        margin: 8,
                        maxSize: new go.Size(160, NaN),
                        wrap: go.TextBlock.WrapFit,
                        //editable: true
                    },
                    new go.Binding("text").makeTwoWay())
            ),
            {doubleClick : function (e, node){nodeDoubleClick(e, node);}}
        );
    }


    myDiagram =
        objGo(go.Diagram, "myDiagramDiv",  // must name or refer to the DIV HTML element
            {
                grid: objGo(go.Panel, "Grid",
                    objGo(go.Shape, "LineH", { stroke: "lightgray", strokeWidth: 0.5 }),
                    objGo(go.Shape, "LineH", { stroke: "gray", strokeWidth: 0.5, interval: 10 }),
                    objGo(go.Shape, "LineV", { stroke: "lightgray", strokeWidth: 0.5 }),
                    objGo(go.Shape, "LineV", { stroke: "gray", strokeWidth: 0.5, interval: 10 })
                ),
                initialContentAlignment: go.Spot.TopCenter, // 居中显示内容
                allowDrop: true,  // must be true to accept drops from the Palette
                "undoManager.isEnabled": true
            });
    myDiagram.nodeTemplate = nodeTemplate

    myDiagram.linkTemplate =
        objGo(go.Link,
            {
                routing: go.Link.Normal,
                curve: go.Link.JumpOver,
            },
            new go.Binding("points").makeTwoWay(),
            objGo(go.Shape,  // the link path shape
                { isPanelMain: true, strokeWidth: 2 }),
            objGo(go.Shape,  // the arrowhead
                { toArrow: "Standard", stroke: null })
        );


    //添加监听线生成事件
    myDiagram.addDiagramListener("LinkDrawn", function(e) {
        //一个节点只能一根线
        var node = myDiagram.findNodeForKey(e.subject.data.from);
        if(node.findLinksOutOf().count>1) {
            myDiagram.model.removeLinkData(e.subject.data);
        }
    })
    //监听节点生成事件
    var nodeIndex=0;
    myDiagram.addDiagramListener("externalobjectsdropped", function(e) {
        e.subject.each(function(n){
            nodeIndex++;
            var nodeData= myDiagram.model.findNodeDataForKey(n.data.key);
            myDiagram.model.setDataProperty(nodeData, 'text', n.data.text+nodeIndex);
            nodeData.json.text=n.data.text+nodeIndex;
        });
    })

    myPalette =
        objGo(go.Palette, "myPaletteDiv",
            {
                maxSelectionCount: 1,
                nodeTemplateMap: myDiagram.nodeTemplateMap,  // share the templates used by myDiagram
                model: new go.GraphLinksModel([  // specify the contents of the Palette
                    Object.create(customNodeTemplate.jenkins),
                    Object.create(customNodeTemplate.apollo),
                    Object.create(customNodeTemplate.sql),
                    Object.create(customNodeTemplate.notify),
                    Object.create(customNodeTemplate.shell)
                ])
            });


    //load();  // load an initial diagram from some JSON text
    myDiagram.model.nodeDataArray=[Object.create(customNodeTemplate.start), Object.create(customNodeTemplate.end)];


}


/***
 * 节点模板
 */
var customNodeTemplate={
    "jenkins":{ text: "jenkins", background: "#99FFFF",fromLinkable:true,toLinkable:true,dialog:"jenkins-dialog",json:null},
    "sql":{ text: "sql升级", background: "#99FFFF",fromLinkable:true,toLinkable:true,dialog:"sql-dialog",json:null},
    "apollo":{ text: "apollo配置", background: "#99FFFF",fromLinkable:true,toLinkable:true,dialog:"apollo-dialog",json:null},
    "notify":{ text: "通知", background: "#99FFFF",fromLinkable:true,toLinkable:true,dialog:"notify-dialog",json:null},
    "shell":{ text: "shell脚本", background: "#99FFFF",fromLinkable:true,toLinkable:true,dialog:"shell-dialog",json:null},
    "start":{ text: "开始", background: "green",loc:"0 0",fromLinkable:true,toLinkable:false,dialog:"start-dialog",json:{type:'start'}},
    "end":{ text: "结束", background: "red",loc:"0 300",fromLinkable:false,toLinkable:true,json:{type:"end"}},
};

/**
 * dialog 配置和json转换
 * @param dialogId
 */
function dialogToJson(dialogId){
    var d = $('#'+dialogId);
    var nodeKey = d.find('[name="key"]').attr('value');
    var node=myDiagram.findNodeForKey(nodeKey);
    var json = (node.part.data.json==null?{}:node.part.data.json);
    d.find('.mydetail [json]').each(function(i){
        var key = $(this).attr('json');
        if(key=='text') {
            var nodeData= myDiagram.model.findNodeDataForKey(nodeKey);
            myDiagram.model.setDataProperty(nodeData, 'text', $(this).val());
            json[key] = $(this).val();
        }
        else if(key=='type'){
            json[key] = $(this).attr('data');
        }else {
            json[key] = $(this).val();
        }
    });
    node.part.data.json=json;
}
function jsonToDialog(dialogId,nodeKey){
    var d = $('#'+dialogId);
    d.find('[name="key"]').attr('value',nodeKey);
    var node=myDiagram.findNodeForKey(nodeKey);
    var json = node.part.data.json;
    if(json==null){
        json={};
    }
    d.find('.mydetail [json]').each(function(i){
        var key = $(this).attr('json');
        if(key=='text'){
            json[key]=node.part.data.text;
        }
        $(this).val(json[key]);
    });
}

/** 保存，加载*/
function modelToJson(){
    var startNode=null;
    myDiagram.nodes.each(function (node) {
        if(node.data.text=='开始'){
            startNode=node;
        }
    });
    node = startNode;
    var nodeJson=[];
    while(node !=null){
        if(node.part.data.json==null){
            alert(node.part.data.text+"节点未配置,请配置后保存！");
            return "error";
        }
        node.part.data.json['location']=node.part.data.loc;
        node.part.data.json['text']=node.part.data.text;

        var firstLink=null;
        node.findLinksOutOf().each(function(link) {
            firstLink=link;
        });
        if(firstLink!=null){
            var next=myDiagram.findNodeForKey(firstLink.data.to);
            node.part.data.json['next']=next.part.data.text;
            nodeJson.push(node.part.data.json);
            node =next;
        }else{
            node.part.data.json['next']=null;
            nodeJson.push(node.part.data.json);
            if(node.part.data.text=='结束'){
                break;
            }
            node=null;
        }
    }
    if(node==null){
        alert('节点必须以结束节点为终止');
        return;
    }
    var j = {};j.nodes=nodeJson;
    return j;
}


/** 当前节点向下查找节点*/
function currentNodeFlowModelToJson(currentNode){
    var startNode=null;
    myDiagram.nodes.each(function (node) {
        //如果等于当前节点，则当前节点作为开始节点向下查找
        if(node.data.text==currentNode.json.text){
            startNode=node;
        }
    });
    node = startNode;
    var nodeJson=[];
    while(node !=null){
        if(node.part.data.json==null){
            alert(node.part.data.text+"节点未配置,请配置后保存！");
            return "error";
        }
        node.part.data.json['location']=node.part.data.loc;
        node.part.data.json['text']=node.part.data.text;

        var firstLink=null;
        node.findLinksOutOf().each(function(link) {
            firstLink=link;
        });
        if(firstLink!=null){
            var next=myDiagram.findNodeForKey(firstLink.data.to);
            node.part.data.json['next']=next.part.data.text;
            nodeJson.push(node.part.data.json);
            node =next;
        }else{
            node.part.data.json['next']=null;
            nodeJson.push(node.part.data.json);
            if(node.part.data.text=='结束'){
                break;
            }
            node=null;
        }
    }
    if(node==null){
        alert('节点必须以结束节点为终止');
        return "error";
    }
    var j = {};j.nodes=nodeJson;
    return j;
}

function jsonToModel(json){
    var model = { "class": "go.GraphLinksModel", "nodeDataArray": [], "linkDataArray": []};
    //add key
    for(var i=0;i<json.nodes.length;i++) {
        var n = json.nodes[i];
        n["key"]=-1-i;
    }
    //画节点
    for(var i=0;i<json.nodes.length;i++) {
        var n = json.nodes[i];
        var type = n['type'];
        var nNode = customNodeTemplate[type];
        nNode['text']=n['text'];
        nNode['loc']=n['location'];
        nNode['json']=n;
        model.nodeDataArray.push(nNode);

    }
    //画线
    for(var i=0;i<json.nodes.length;i++) {
        var n = json.nodes[i];
        var findTo=null;
        for(var j=0;j<json.nodes.length;j++) {
            var n2 = json.nodes[j];
            if(n2["text"]==n["next"]){
                findTo=n2;
            }
        }
        if(findTo!=null){
            model.linkDataArray.push({"from":n["key"],"to":findTo["key"]});
        }
    }
    return model;
}